function mxPrintPreview(graph, scale, pageFormat, x0, y0, border, title, pageSelector)
{
	x0 = (x0 != null) ? x0 : 0;
	y0 = (y0 != null) ? y0 : 0;
	scale = (scale != null) ? scale : 1;
	pageSelector = (pageSelector != null) ? pageSelector : true;

	if (pageFormat == null)
	{
		pageFormat = mxConstants.PAGE_FORMAT_A4_PORTRAIT;
	}
	this.graph = graph;
	this.pageFormat = pageFormat;
	this.scale = scale;
	this.x0 = x0;
	this.y0 = y0;
	this.border = border;
	this.title = title;
	this.pageSelector = pageSelector;
};
mxPrintPreview.prototype.graph = null;
mxPrintPreview.prototype.pageFormat = null;
mxPrintPreview.prototype.scale = null;
mxPrintPreview.prototype.x0 = null;
mxPrintPreview.prototype.y0 = null;
mxPrintPreview.prototype.border = null;
mxPrintPreview.prototype.title = null;
mxPrintPreview.prototype.pageSelector = null;
mxPrintPreview.prototype.wnd = null;
mxPrintPreview.prototype.getWindow = function()
{
	return this.wnd;
};
mxPrintPreview.prototype.open = function(css)
{
	if (this.wnd == null)
	{
		this.wnd = window.open();
		var doc = this.wnd.document;
		doc.writeln('<html>');
		doc.writeln('<head>');

		if (this.title != null)
		{
			doc.writeln('<title>' + this.title + '</title>');
		}
		doc.writeln('<style type="text/css">');
		doc.writeln('@media print {');
		doc.writeln('  table.mxPageSelector { display: none; }');
		doc.writeln('  hr { display: none; }');
		doc.writeln('}');
		doc.writeln('@media screen {');

		doc.writeln('  table.mxPageSelector { position: fixed; right: 10px; top: 10px;'
			+ 'font-family: Arial; font-size:10pt; color: gray; border-color: gray;' + 'background: white;}');
		doc.writeln('  body { background: gray; }');
		doc.writeln('}');

		if(css != null)
		{
			doc.writeln(css);
		}
		doc.writeln('</style>');
		doc.writeln('</head>');
		doc.writeln('<body>');
		mxClient.link('stylesheet', mxClient.basePath + 'css/common.css', doc);

		if (mxClient.IS_IE)
		{
			doc.namespaces.add("v", "urn:schemas-microsoft-com:vml");
			doc.namespaces.add("o", "urn:schemas-microsoft-com:office:office");
			mxClient.link('stylesheet', mxClient.basePath + 'css/explorer.css', doc);
		}
		var bounds = this.graph.getGraphBounds().clone();
		var sc = this.graph.getView().getScale() / this.scale;
		var tr = this.graph.getView().getTranslate();

		bounds.x /= sc;
		bounds.x -= tr.x - this.x0;
		bounds.y /= sc;
		bounds.y -= tr.y - this.y0;
		bounds.width /= sc;
		bounds.height /= sc;
		var hpages = Math.max(1, Math.ceil((bounds.x + bounds.width) / this.pageFormat.width));
		var vpages = Math.max(1, Math.ceil((bounds.y + bounds.height) / this.pageFormat.height));

		for (var i = 0; i < vpages; i++)
		{
			dy = i * this.pageFormat.height / this.scale;

			for (var j = 0; j < hpages; j++)
			{
				dx = j * this.pageFormat.width / this.scale;
				var div = this.renderPage(this.pageFormat.width, this.pageFormat.height, -dx, -dy, this.scale);
				var pageNum = i * hpages + j + 1;
				div.setAttribute('id', 'mxPage-' + pageNum);

				if (this.border != null)
				{
					div.style.borderColor = this.border;
					div.style.borderStyle = 'solid';
					div.style.borderWidth = '1px';
				}

				div.style.background = 'white';

				if (i < vpages - 1 || j < hpages - 1)
				{
					div.style.pageBreakAfter = 'always';
				}

				if (mxClient.IS_IE)
				{

					doc.writeln(div.outerHTML);
					div.parentNode.removeChild(div);
				}
				else
				{
					div.parentNode.removeChild(div);
					doc.body.appendChild(div);
				}

				if (i < vpages - 1 || j < hpages - 1)
				{
					doc.body.appendChild(doc.createElement('hr'));
				}
			}
		}
		doc.writeln('</body>');
		doc.writeln('</html>');
		doc.close();
		mxEvent.release(doc.body);

		if (this.pageSelector && (vpages > 1 || hpages > 1))
		{
			var table = this.createPageSelector(vpages, hpages);
			doc.body.appendChild(table);

			if (mxClient.IS_IE)
			{
				table.style.position = 'absolute';
				var update = function()
				{
					table.style.top = (doc.body.scrollTop + 10) + 'px';
				};
				mxEvent.addListener(this.wnd, 'scroll', function(evt)
				{
					update();
				});
				mxEvent.addListener(this.wnd, 'resize', function(evt)
				{
					update();
				});
			}
		}
	}
	this.wnd.focus();
	return this.wnd;
};
mxPrintPreview.prototype.createPageSelector = function(vpages, hpages)
{
	var doc = this.wnd.document;
	var table = doc.createElement('table');
	table.className = 'mxPageSelector';
	table.setAttribute('border', '1');
	var tbody = doc.createElement('tbody');

	for (var i = 0; i < vpages; i++)
	{
		var row = doc.createElement('tr');

		for (var j = 0; j < hpages; j++)
		{
			var cell = doc.createElement('td');
			cell.style.cursor = 'pointer';
			var pageNum = i * hpages + j + 1;
			mxUtils.write(cell, pageNum, doc);
			this.addPageClickListener(cell, pageNum);
			row.appendChild(cell);
		}
		tbody.appendChild(row);
	}
	table.appendChild(tbody);
	return table;
};
mxPrintPreview.prototype.addPageClickListener = function(cell, pageNumber)
{
	var self = this;
	mxEvent.addListener(cell, 'click', function(evt)
	{
		var page = self.wnd.document.getElementById('mxPage-' + pageNumber);

		if (page != null)
		{
			self.wnd.scrollTo(0, Math.max(0, page.offsetTop - 8));
		}
	});
};
mxPrintPreview.prototype.renderPage = function(w, h, dx, dy, scale)
{
	var div = document.createElement('div');
	div.style.width = w + 'px';
	div.style.height = h + 'px';
	div.style.overflow = 'hidden';
	div.style.pageBreakInside = 'avoid';
	document.body.appendChild(div);
	var view = this.graph.getView();
	var previousContainer = this.graph.container;
	this.graph.container = div;
	var canvas = view.getCanvas();
	var backgroundPane = view.getBackgroundPane();
	var drawPane = view.getDrawPane();
	var overlayPane = view.getOverlayPane();

	if (this.graph.dialect == mxConstants.DIALECT_SVG)
	{
		view.createSvg();
	}
	else if (this.graph.dialect == mxConstants.DIALECT_VML)
	{
		view.createVml();
	}
	else
	{
		view.createHtml();
	}
	var eventsEnabled = view.isEventsEnabled();
	view.setEventsEnabled(false);
	var graphEnabled = this.graph.isEnabled();
	this.graph.setEnabled(false);
	var translate = view.getTranslate();
	view.translate = new mxPoint(dx, dy);
	var temp = null;

	try
	{

		var model = this.graph.getModel();
		var cells = [model.getRoot()];
		temp = new mxTemporaryCellStates(view, scale, cells);
	}
	finally
	{
		if (mxClient.IS_IE)
		{
			view.overlayPane.innerHTML = '';
		}
		else
		{
			var tmp = div.firstChild;

			while (tmp != null)
			{
				var next = tmp.nextSibling;

				if (tmp.nodeName.toLowerCase() != 'svg' && tmp.style.cursor != 'default')
				{
					tmp.parentNode.removeChild(tmp);
				}
				tmp = next;
			}
		}
		view.overlayPane.parentNode.removeChild(view.overlayPane);
		this.graph.setEnabled(graphEnabled);
		this.graph.container = previousContainer;
		view.canvas = canvas;
		view.backgroundPane = backgroundPane;
		view.drawPane = drawPane;
		view.overlayPane = overlayPane;
		view.translate = translate;
		temp.destroy();
		view.setEventsEnabled(eventsEnabled);
	}
	return div;
};
mxPrintPreview.prototype.print = function()
{
	this.open();
	this.wnd.print();
};
mxPrintPreview.prototype.close = function()
{
	if (this.wnd != null)
	{
		this.wnd.close();
		this.wnd = null;
	}
};